
#include <iostream>
#include "CameraController.h"

#include "InputsManager.h"

/*
void LooppingLayer::draw()
{
	CCLayer::draw();

	ccDrawSquare( Bound.origin , CCPoint(Bound.origin.x + Bound.size.width, Bound.origin.y + Bound.size.height ) );
}
*/

void LooppingLayer::visit()
{
	if( cam == NULL )
	{
		CCLayer::visit();
		return;
	}

	CCSize ScreenSize = CCDirector::sharedDirector()->getWinSize();
	CCRect  BackupBound = Bound;
	CCPoint BackupPos = getPosition();
	CCPoint _worldPos = getPosition();

	_worldPos = CCPoint( _worldPos.x + Bound.origin.x,  _worldPos.y + Bound.origin.y );

	if( loopX )
		_worldPos.x = (((int)_worldPos.x)%((int)Bound.size.width));
	
	if( loopY )
		_worldPos.y = (((int)_worldPos.y)%((int)Bound.size.height));


	if( loopX )
	{
		if( _worldPos.x > 0.0f )
		{
			setPosition( CCPoint( _worldPos.x - Bound.size.width, _worldPos.y ) );
			CCLayer::visit();
		}
		if( (_worldPos.x + Bound.size.width) < ( ScreenSize.width ) )
		{
			setPosition( CCPoint( _worldPos.x + Bound.size.width, _worldPos.y ) );
			CCLayer::visit();
		}
	}

	if( loopY )
	{
		if( _worldPos.y > 0.0f  )
		{
			setPosition( CCPoint( _worldPos.x , _worldPos.y - Bound.size.height ) );
			CCLayer::visit();
		}
		if( ((_worldPos.y + Bound.size.height) < ( ScreenSize.height )) )
		{
			setPosition( CCPoint( _worldPos.x, _worldPos.y + Bound.size.height ) );
			CCLayer::visit();
		}
	}

	if( loopX  && loopY )
	{
		if( (_worldPos.x > 0.0f)  && (_worldPos.y > 0.0f) )
		{
			setPosition( CCPoint( _worldPos.x - Bound.size.width, _worldPos.y - Bound.size.height) );
			CCLayer::visit();
		}
		if( ((_worldPos.x + Bound.size.width) < ( ScreenSize.width )) && (_worldPos.y > 0.0f) )
		{
			setPosition( CCPoint( _worldPos.x + Bound.size.width, _worldPos.y - Bound.size.height) );
			CCLayer::visit();
		}

		if( (_worldPos.x > 0.0f) && ((_worldPos.y + Bound.size.height) < ( ScreenSize.height ))  )
		{
			setPosition( CCPoint( _worldPos.x - Bound.size.width , _worldPos.y + Bound.size.height ) );
			CCLayer::visit();
		}
		if( ((_worldPos.x + Bound.size.width) < ( ScreenSize.width )) && ((_worldPos.y + Bound.size.height) < ( ScreenSize.height )) )
		{
			setPosition( CCPoint( _worldPos.x + Bound.size.width, _worldPos.y + Bound.size.height ) );
			CCLayer::visit();
		}
	}

	setPosition( _worldPos );
	CCLayer::visit();

	setPosition( BackupPos );
	Bound = BackupBound;
}

///

CameraController::CameraController()
{
	mCameraLayer = NULL;
	mCamPos = CCPoint(0.0f, 0.0f);
	bPosDirty = false;
	mTarget = NULL;

	//mUnfollowZone = CCRect( 180.0f, 100.0f, 110.0f, 140.0f );
	mUnfollowZone = CCRect( 240.0f, 160.0f, 1.0f, 1.0f );
}

///

CameraController::~CameraController()
{
	if( mCameraLayer )
	{
		if( ! mCameraLayer->getParent() )
			delete mCameraLayer;
	}
}

///

CCLayer* CameraController::init( CCLayer* _layerCamera )
{
	if( ! _layerCamera )
	{
		_layerCamera = new CCLayer();
		_layerCamera->init();
	}

	mCameraLayer = _layerCamera;
	bPosDirty = true;

	return _layerCamera;
}

///

CCLayer* CameraController::addParralaxLayer( CCPoint& _coefSpeed, bool _loopX, bool _loopY, CCRect _rect , CCLayer* _layer )
{
	assert( mCameraLayer );

	if( ! _layer )
	{
		_layer = new LooppingLayer(_loopX, _loopY, _rect, this );
		_layer->init();
	}

	for( int i=0; i< MAX_PARRALAX_LAYER; i++ )
	{
		if( mParralaxLayers[i].layer == NULL )
		{
			
			mParralaxLayers[i] = ParralaxLayer( _layer, _coefSpeed );
			mCameraLayer->getParent()->addChild( _layer, MAX_PARRALAX_LAYER  - i + 1, PARRALAX_TAG_0 + i );
			
			return _layer;
		}
	}

	assert( false );
	return NULL;
}

///

void CameraController::update( ccTime dt )
{
	if( bPosDirty )
		RefreshPosition();

	if( mTarget )
		followTarget();
}

///

void CameraController::followTarget()
{
    CCPoint _pos = mTarget->getPosition();
	CCPoint posToFollowInScreen = getScreenPos( _pos );


	if( posToFollowInScreen.x > CCRect::CCRectGetMaxX(mUnfollowZone) )
	{
		mCamPos.x -=  CCRect::CCRectGetMaxX(mUnfollowZone) - posToFollowInScreen.x;
		RefreshPosition();
	}
	else if( posToFollowInScreen.x <  CCRect::CCRectGetMinX(mUnfollowZone) )
	{
		mCamPos.x += posToFollowInScreen.x - CCRect::CCRectGetMinX(mUnfollowZone);
		RefreshPosition();
	}

	if( posToFollowInScreen.y > CCRect::CCRectGetMaxY(mUnfollowZone) )
	{
		mCamPos.y -=  CCRect::CCRectGetMaxY(mUnfollowZone) - posToFollowInScreen.y;
		RefreshPosition();
	}
	else if( posToFollowInScreen.y <  CCRect::CCRectGetMinY(mUnfollowZone) )
	{
		mCamPos.y += posToFollowInScreen.y - CCRect::CCRectGetMinY(mUnfollowZone);
		RefreshPosition();
	}
}

///

void CameraController::setCamPos( b2Vec2& _pos )
{
	setCamPos( _pos.x, _pos.y );
}

///

void CameraController::setCamPos( CCPoint& _pos )
{
	setCamPos( _pos.x, _pos.y );
}

///

void CameraController::setCamPos( float x, float y)
{
	mCamPos = CCPoint( x, y);
	bPosDirty = true;
}

///

void CameraController::RefreshPosition()
{
	if( mCameraLayer )
	{
		CCPoint _scale = CCPoint( mCameraLayer->getScaleX(), mCameraLayer->getScaleY() );
		CCPoint _realPos = CCPoint( (mCamPos.x*_scale.x), (mCamPos.y*_scale.y) );

		mCameraLayer->setPosition( CCPoint( (_realPos.x) * (-1.0f) , (_realPos.y) * (-1.0f) ) );

		for( int i=0; i<MAX_PARRALAX_LAYER; i++ )
		{
			if( mParralaxLayers[i].layer )
			{
				mParralaxLayers[i].layer->setScaleX( mCameraLayer->getScaleX() );
				mParralaxLayers[i].layer->setScaleY( mCameraLayer->getScaleY() );

				mParralaxLayers[i].layer->setPosition( CCPoint( (_realPos.x*mParralaxLayers[i].speedCoef.x) * (-1.0f) , (_realPos.y*mParralaxLayers[i].speedCoef.y) * (-1.0f) ) );
				//mParralaxLayers[i].layer->setPosition( CCPoint( (mCamPos.x* (mParralaxLayers[i].speedCoef.x - 1.0f) ) * (-1.0f) , (mCamPos.y*(mParralaxLayers[i].speedCoef.y - 1.0f)) * (-1.0f) ) );
			}
		}

		bPosDirty = false;
	}
}

///

CCLayer* CameraController::getParralaxLayer( int _id )
{
	if( _id > MAX_PARRALAX_LAYER )
	{
		printf("you try to get a parralax layer over the limit, change the limit or change your needs in layer \n" );
		assert( false );
	}

	if( mParralaxLayers[_id].layer == NULL )
	{
		printf("the layer your try to get doesnt exist, be sure to create them before \n" );
		assert( false );
	}

	return mParralaxLayers[_id].layer;
}

///

CCPoint CameraController::getScreenPos( CCNode* _node )
{
    CCPoint _pos = _node->getPosition();
	return getScreenPos( _pos );
}

///

CCPoint CameraController::getScreenPos( CCPoint& _pos )
{
	if( bPosDirty )
		RefreshPosition();

	return CCPoint( _pos.x - mCamPos.x ,  _pos.y - mCamPos.y );
}

///


b2Vec2 CameraController::getScreenPos( b2Body* __body )
{
	return getScreenPos( __body->GetPosition() );
}

///

b2Vec2 CameraController::getScreenPos( const b2Vec2& _pos )
{
	if( bPosDirty )
		RefreshPosition();

	return b2Vec2( (_pos.x * PTM_RATIO) - mCamPos.x ,  (_pos.y * PTM_RATIO) - mCamPos.y );
}

///
